﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EasyCLRBase.AssemblyExplorer;
using Microsoft.SqlServer.Server;
using EasyCLRBase;
using System.Data.SqlTypes;

namespace ClrFileSystem
{
    [Export(typeof(EasyCLRBase.IClrBase))]
    [Serializable]
    [SqlUserDefinedType(
        Format.UserDefined,
        Name = "ZipFile",
        IsByteOrdered = true,
        ValidationMethodName = "Validate",
        MaxByteSize = Constants.UserDefinedTypeMaxByte)]
    public struct DotNetZipStruct : IClrBase, IBinarySerialize, INullable, IEquatable<DotNetZipStruct>
    {
        private Boolean _IsNull;
        private String _File;

        private String _Password;
        private String _Comment;
        private byte _CompressionLevel;
        private byte _CompressionMethod;
        private byte _EncryptionAlgorithm;
        private byte _ExtractionAction;

        private Ionic.Zip.ZipFile _IonicZipFile;

        [SqlFacet(MaxSize = -1)]
        public String File
        {
            get { return this._File; }
            set
            {
                this._IsNull = string.IsNullOrEmpty(value);
                this._File = value;
            }
        }


        [SqlFacet(MaxSize = -1)]
        public String Info
        {
            get
            {
                this.Validate();
                return this._IonicZipFile.Info;
            }
        }

        #region Options
        /// <summary>
        /// ZIP File Password
        /// </summary>
        [SqlFacet(MaxSize = -1)]
        public String Password
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._Password;
            }
            set
            {
                this._Password = value;
            }
        }
        /// <summary>
        /// ZIP File Comment
        /// </summary>
        [SqlFacet(MaxSize = -1)]
        public String Comment
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._Comment;
            }
            set
            {
                this._Comment = value;
            }
        }
        /// <summary>
        /// ZIP File Compression Level
        /// </summary>
        public byte CompressionLevel
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._CompressionLevel;
            }
            set
            {
                this._CompressionLevel = value;
            }
        }
        /// <summary>
        /// ZIP File Compression Method
        /// </summary>
        public byte CompressionMethod
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._CompressionMethod;
            }
            set
            {
                this._CompressionMethod = value;
            }
        }
        /// <summary>
        /// ZIP File Encryption Algorithm
        /// </summary>
        public byte EncryptionAlgorithm
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._EncryptionAlgorithm;
            }
            set
            {
                this._EncryptionAlgorithm = value;
            }
        }
        /// <summary>
        /// ZIP File Encryption Algorithm
        /// </summary>
        public byte ExtractionAction
        {
            [SqlMethod(IsDeterministic = true)]
            get
            {
                return this._ExtractionAction;
            }
            set
            {
                this._ExtractionAction = value;
            }
        }

        [SqlMethod(IsDeterministic = true, IsMutator = true)]
        public void SetOptions(
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString password,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString comment,
            [SqlFacet(IsNullable = true)]SqlByte compressionLevel,
            [SqlFacet(IsNullable = true)]SqlByte compressionMethod,
            [SqlFacet(IsNullable = true)]SqlByte encryptionAlgorithm,
            [SqlFacet(IsNullable = true)]SqlByte extractionAction
         )
        {
            this.CompressionLevel = (compressionLevel.IsNull == false) ? compressionLevel.Value : (byte)Ionic.Zlib.CompressionLevel.Default;
            this.CompressionMethod = (compressionMethod.IsNull == false) ? compressionMethod.Value : (byte)Ionic.Zip.CompressionMethod.BZip2;
            this.Comment = (comment.IsNull == false) ? comment.Value : null;
            this.EncryptionAlgorithm = (encryptionAlgorithm.IsNull == false) ? encryptionAlgorithm.Value : (byte)Ionic.Zip.EncryptionAlgorithm.None;
            this.Password = (password.IsNull == false) ? password.Value : null;
            this.ExtractionAction = (extractionAction.IsNull == false) ? extractionAction.Value : (byte)Ionic.Zip.ExtractExistingFileAction.Throw;
        }

        #endregion

        private void Dispose()
        {
            if (this._IonicZipFile != null)
            {
                this._IonicZipFile.Dispose();
            }
        }

        /// <summary>
        /// Add a specific directory to the zip container.
        /// </summary>
        /// <param name="path">Directory Path.</param>
        /// <param name="baseDir">Base directory in the archive.</param>
        /// <returns>Number of added files into archive.</returns>
        public int AddDirectory(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString baseDir
        )
        {
            if (path.IsNull == true || String.IsNullOrEmpty(path.Value) == true)
                throw new ArgumentNullException("path", "Could not add file to zip container. File name is not correctly given.");
            int result = 0;
            try
            {
                if (baseDir.IsNull == true)
                    baseDir = String.Empty;
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.AddDirectory(path.Value, baseDir.Value);
                    zip.Save();
                    result = zip.Entries.Count - result;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }

        /// <summary>
        /// Add a specific file to the zip container.
        /// </summary>
        /// <param name="path">Full file name</param>
        /// <param name="baseDir">Directory Name in the archive.</param>
        /// <returns>Amount of added file(s).</returns>
        public int AddFile(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString baseDir
        )
        {
            if (path.IsNullOrEmpty() == true)
                throw new ArgumentNullException("path", "Could not add file to zip container. File name is not correctly given.");
            int result = 0;
            try
            {
                if (baseDir.IsNull == true)
                    baseDir = String.Empty;
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    String fname = System.IO.Path.GetFileName(path.Value);
                    if (baseDir.Value.Length > 0)
                        fname = String.Concat(baseDir.Value, System.IO.Path.AltDirectorySeparatorChar, fname);

                    if (zip.Where(a => String.Equals(a.FileName, fname)).Count() > 0)
                        zip.UpdateFile(path.Value, baseDir.Value);
                    else
                        zip.AddFile(path.Value, baseDir.Value);
                    zip.Save();
                    result = zip.Entries.Count - result;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Add a files specified by filter.
        /// </summary>
        /// <param name="path">Directory path.</param>
        /// <param name="baseDir">Directory Name in the archive.</param>
        /// <param name="criteria">Add criteria.</param>
        /// <param name="recurseDir"></param>
        /// <returns></returns>
        public int AddSelectedFiles(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString criteria,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString baseDir,
            [SqlFacet(IsNullable = true)]SqlBoolean recurseDir
        )
        {
            if (path.IsNullOrEmpty() == true)
                throw new ArgumentNullException("path", "Could not add directory to zip container. Directory name is not correctly given.");
            int result = 0;
            try
            {
                if (criteria.IsNull == true)
                    criteria = "*";
                if (baseDir.IsNull == true)
                    baseDir = String.Empty;
                if (recurseDir.IsNull)
                    recurseDir = false;

                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.AddSelectedFiles(criteria.Value, path.Value, baseDir.Value, recurseDir.Value);
                    zip.Save();
                    result = zip.Entries.Count - result;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Add new Entry by string.
        /// </summary>
        /// <param name="entryName">Name of the file</param>
        /// <param name="content">File Content as String</param>
        /// <returns></returns>
        [SqlMethod(OnNullCall = false)]
        public int AddStringEntry(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString entryName,
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString content
        )
        {
            if (entryName.IsNullOrEmpty() == true)
                throw new ArgumentNullException("entryName", "Could not add file to zip container. File name is not correctly given.");
            if (content.IsNullOrEmpty() == true)
                throw new ArgumentNullException("content", "Could not add file to zip container. File Content is empty.");
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    String fname = System.IO.Path.GetFileName(entryName.Value);
                    if (zip.Where(a => String.Equals(a.FileName, fname)).Count() > 0)
                        zip.UpdateEntry(entryName.Value, content.Value);
                    else
                        zip.AddEntry(entryName.Value, content.Value);
                    zip.Save();
                    result = zip.Entries.Count - result;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Add new Entry by bytes
        /// </summary>
        /// <param name="entryName">Name of the file</param>
        /// <param name="content">File Content as Byte Array</param>
        /// <returns></returns>
        [SqlMethod(OnNullCall = false)]
        public int AddBytesEntry(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString entryName,
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlBinary content
        )
        {
            if (entryName.IsNullOrEmpty() == true)
                throw new ArgumentNullException("entryName", "Could not add file to zip container. File name is not correctly given.");
            if (content.IsNullOrEmpty() == true)
                throw new ArgumentNullException("content", "Could not add file to zip container. File Content is empty.");
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    if (zip.ContainsEntry(entryName.Value) == true)//zip.Where(a => String.Equals(a.FileName, fname)).Count() > 0)
                        zip.UpdateEntry(entryName.Value, content.Value);
                    else
                        zip.AddEntry(entryName.Value, content.Value);
                    zip.Save();
                    result = zip.Entries.Count - result;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        [SqlMethod(OnNullCall = false)]
        public int RemoveEntry(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString fileName
        )
        {
            if (fileName.IsNullOrEmpty() == true)
                throw new ArgumentNullException("fileName", "File Name cannot be null or empty.");
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.RemoveEntry(fileName.Value);
                    zip.Save();
                    result = result - zip.Entries.Count;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Remove entries from archive by criteria.
        /// </summary>
        /// <param name="removeCriteria">criteria to remove.</param>
        /// <returns>Number of deleted files in archive.</returns>
        [SqlMethod(OnNullCall = false)]
        public int RemoveSelectedEntries(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString removeCriteria
        )
        {
            if (removeCriteria.IsNullOrEmpty() == true)
                throw new ArgumentNullException("removeCriteria", "Parameter cannot be null or empty");
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.RemoveSelectedEntries(removeCriteria.Value);
                    zip.Save();
                    result = result - zip.Entries.Count;
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Extract all files
        /// </summary>
        /// <param name="path">path to extract</param>
        /// <returns>count of extracted files</returns>
        [SqlMethod(OnNullCall = false)]
        public int Extract(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path
        )
        {
            if (path.IsNullOrEmpty() == true)
                throw new ArgumentNullException("path", "No extraction path given.");
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.ExtractAll(path.Value, (Ionic.Zip.ExtractExistingFileAction)this._ExtractionAction);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Extract all files
        /// </summary>
        /// <param name="path">path to extract</param>
        /// <returns>count of extracted files</returns>
        public int ExtractSelectedEntries(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString selectionCriteria,
            [SqlFacet(MaxSize = -1, IsNullable = true)]SqlString directoryPathInArchive,
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path
        )
        {
            if (path.IsNullOrEmpty() == true)
                throw new ArgumentNullException("path", "No extraction path given.");
            if (selectionCriteria.IsNullOrEmpty() == true)
                throw new ArgumentNullException("selectionCriteria", "Criteria cannot be null or empty.");
            if (directoryPathInArchive.IsNullOrEmpty() == true)
                directoryPathInArchive = String.Empty;
            int result = 0;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.Entries.Count;
                    zip.ExtractSelectedEntries(selectionCriteria.Value, directoryPathInArchive.Value, path.Value, (Ionic.Zip.ExtractExistingFileAction)this._ExtractionAction);
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }
        /// <summary>
        /// Extract only one file.
        /// </summary>
        /// <param name="path">Path where to extract</param>
        /// <param name="fileNameToExtract">File Name that should be in the zip container.</param>
        [SqlMethod(IsMutator = true)]
        public void ExtractOne(
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString path,
            [SqlFacet(MaxSize = -1, IsNullable = false)]SqlString fileNameToExtract
        )
        {
            if (path.IsNullOrEmpty() == true)
                throw new ArgumentNullException("path", "No extraction path given.");
            try
            {
                this.Validate();

                using (var zip = this._IonicZipFile)
                {
                    if (zip.ContainsEntry(fileNameToExtract.Value) == true)
                        zip[fileNameToExtract.Value].Extract(path.Value, (Ionic.Zip.ExtractExistingFileAction)this._ExtractionAction);
                    else
                        throw new Exception("File to extract is not in the container");
                }
            }
            catch
            {
                throw;
            }
            finally
            {
                this.Dispose();
            }
        }
        /// <summary>
        /// Check if centain file contains in the archive.
        /// </summary>
        /// <param name="entry">Entry to check, if it contains in the Zip File.</param>
        /// <returns></returns>
        [SqlMethod(OnNullCall = false)]
        public Boolean Contains([SqlFacet(MaxSize = -1, IsNullable = false)]SqlString entry)
        {
            Boolean result = false;
            try
            {
                this.Validate();
                using (var zip = this._IonicZipFile)
                {
                    result = zip.ContainsEntry(entry.Value);
                }
            }
            finally
            {
                this.Dispose();
            }
            return result;
        }

        /// <summary>
        /// Validate if DotNetZipStruct have correct given path.
        /// </summary>
        /// <returns></returns>
        private Boolean Validate()
        {
            if (String.IsNullOrEmpty(this.File) == true)
                throw new ArgumentNullException("No zip file has given.");

            this._IonicZipFile = new Ionic.Zip.ZipFile(this.File)
            {
                CompressionLevel = (Ionic.Zlib.CompressionLevel)this.CompressionLevel,
                CompressionMethod = (Ionic.Zip.CompressionMethod)this.CompressionMethod,
                Encryption = (Ionic.Zip.EncryptionAlgorithm)this.EncryptionAlgorithm,
                Password = this.Password,
                Comment = this.Comment
            };
            return true;
        }
        /// <summary>
        /// Need for CLR User Defined Types.
        /// </summary>
        /// <returns>Zip File Name.</returns>
        #region Inherited
        /// <summary>
        /// Create a string with "howto" use this UDT
        /// </summary>
        /// <returns></returns>
        [return: SqlFacet(MaxSize = -1)]
        public String HowTo()
        {
            String result = String.Concat(
                "Howto Parse the type:", Environment.NewLine,
                "You can pass any String to parse it to ZipFile like", Environment.NewLine,
                @"declare @zip ZipFile = 'C:\CLR\compressed.zip'", Environment.NewLine,
                "There is no validation for correct path.", Environment.NewLine,
                "Accessible Properties Read (or) Write: ", Environment.NewLine,
                "select @zip.[File], @zip.[Info], @zip.[Password], @zip.[Comment], @zip.[CompressionLevel], @zip.[CompressionMethod], @zip.[EncryptionAlgorithm]", Environment.NewLine, Environment.NewLine,

                "To set new Path three ways: ", Environment.NewLine,
                @"1) set @zip = ZipFile::Parse('C:\CLR\compressed2.zip')", Environment.NewLine,
                @"2) set @zip = 'C:\CLR\compressed2.zip'", Environment.NewLine,
                @"3) set @zip.[File] = 'C:\CLR\compressed2.zip'", Environment.NewLine, Environment.NewLine,
                "@zip.[Info] is Readonly Property. It return the nvarchar(max) with full description of all entries in the Container.", Environment.NewLine, Environment.NewLine,

                "Methods in the UDT:", Environment.NewLine,
                "set @zip.SetOptions(password nvarchar(max) = null, comment nvarchar(max) = null, compressionLevel tinyint = null, compressionMethod tinyint = null, encryptionAlgorithm tinyint = null, extractionAction tinyint = null) => is Mutator and can be used with set command. ", Environment.NewLine,
                "The return value of all Add methods is a count of added files to the container.", Environment.NewLine,
                "print @zip.AddFile(path nvarchar(max), baseDir nvarchar(max) = null) => Add a file to the Zip Container. If File exists Update it. Base Directory is optional. ", Environment.NewLine,
                "print @zip.AddSelectedFiles(path nvarchar(max), criteria nvarchar(max) = null, baseDir nvarchar(max) = null) => add multiple files from given path (as directory) and optional searching filter (is null => *). Base Directory is optional too and will be set to empty string.", Environment.NewLine, Environment.NewLine,

                "Copy of official docu from http://dotnetzip.codeplex.com/SourceControl/latest#Zip/ZipFile.Selector.cs", Environment.NewLine,
                @"This method selects files from the the current working directory matching the specified criteria, and adds them to the ZipFile.

                Specify the criteria in statements of 3 elements: a noun, an operator, and
                a value.  Consider the string ""name != *.doc"" .  The noun is ""name"".  The
                operator is ""!="", implying ""Not Equal"".  The value is ""*.doc"".  That
                criterion, in English, says ""all files with a name that does not end in
                the .doc extension.""

                Supported nouns include ""name"" (or ""filename"") for the filename; ""atime"",
                ""mtime"", and ""ctime"" for last access time, last modfied time, and created
                time of the file, respectively; ""attributes"" (or ""attrs"") for the file
                attributes; ""size"" (or ""length"") for the file length (uncompressed), and
                ""type"" for the type of object, either a file or a directory.  The
                ""attributes"", ""name"" and ""type"" nouns both support = and != as operators.
                The ""size"", ""atime"", ""mtime"", and ""ctime"" nouns support = and !=, and
                &gt;, &gt;=, &lt;, &lt;= as well. The times are taken to be expressed in
                local time.

                Specify values for the file attributes as a string with one or more of the
                characters H,R,S,A,I,L in any order, implying file attributes of Hidden,
                ReadOnly, System, Archive, NotContextIndexed, and ReparsePoint (symbolic
                link) respectively.

                To specify a time, use YYYY-MM-DD-HH:mm:ss or YYYY/MM/DD-HH:mm:ss as the
                format.  If you omit the HH:mm:ss portion, it is assumed to be 00:00:00
                (midnight).

                The value for a size criterion is expressed in integer quantities of bytes,
                kilobytes (use k or kb after the number), megabytes (m or mb), or gigabytes
                (g or gb).

                The value for a name is a pattern to match against the filename, potentially
                including wildcards.  The pattern follows CMD.exe glob rules: * implies one
                or more of any character, while ?  implies one character.  If the name
                pattern contains any slashes, it is matched to the entire filename,
                including the path; otherwise, it is matched against only the filename
                without the path.  This means a pattern of ""*\*.*"" matches all files one
                directory level deep, while a pattern of ""*.*"" matches all files in all
                directories.

                To specify a name pattern that includes spaces, use single quotes around the
                pattern.  A pattern of ""'* *.*'"" will match all files that have spaces in
                the filename.  The full criteria string for that would be ""name = '* *.*'"" .

                The value for a type criterion is either F (implying a file) or D (implying
                a directory).

                Some examples:
                name != *.xls 
                any file with an extension that is not .xls

                name = *.mp3 
                any file with a .mp3 extension.

                *.mp3
                (same as above) any file with a .mp3 extension.

                attributes = A 
                all files whose attributes include the Archive bit.

                attributes != H 
                all files whose attributes do not include the Hidden bit.
                
                mtime > 2009-01-01
                all files with a last modified time after January 1st, 2009.
                
                size > 2gb
                all files whose uncompressed size is greater than 2gb.

                type = D
                all directories in the filesystem. ",


                Environment.NewLine, Environment.NewLine,
                "print @zip.AddStringEntry(entryName nvarchar(max), content nvarchar(max)) => add a file based on the entry name (as file name) and it content as nvarchar(max).", Environment.NewLine,
                "print @zip.AddBytesEntry(entryName nvarchar(max), content varbinary(max)) => add a file based on the entry name (as file name) and it content as varbinary(max).", Environment.NewLine,
                "set @zip.ExtractOne(path nvarchar(max), fileNameToExtract nvarchar(max)) => extract one file in the container to specific path. Return the ZipFile Type.", Environment.NewLine,
                "print @zip.Extract(path nvarchar(max)) => extract all files in the container to specific path.", Environment.NewLine,

                "print ZipFile::Help() => print HowTo Information for the zipfile.", Environment.NewLine,

                @"set @zip.AddItem('C:\CLR')", Environment.NewLine,
                "Compression Level valid values: ", Environment.NewLine, " - ", String.Join(String.Concat(Environment.NewLine, " - "), new Ionic.Zlib.CompressionLevel().GetEnumeratorDefinitionString()), Environment.NewLine, Environment.NewLine,
                "Compression Method valid values: ", Environment.NewLine, " - ", String.Join(String.Concat(Environment.NewLine, " - "), new Ionic.Zip.CompressionMethod().GetEnumeratorDefinitionString()), Environment.NewLine, Environment.NewLine,
                "Encryption Algorithm valid values: ", Environment.NewLine, " - ", String.Join(String.Concat(Environment.NewLine, " - "), new Ionic.Zip.EncryptionAlgorithm().GetEnumeratorDefinitionString()), Environment.NewLine, Environment.NewLine,
                "Extraction Action valid values: ", Environment.NewLine, " - ", String.Join(String.Concat(Environment.NewLine, " - "), new Ionic.Zip.ExtractExistingFileAction().GetEnumeratorDefinitionString()), Environment.NewLine, Environment.NewLine
            );

            return result;
        }
        /// <summary>
        /// Override to String, needed for MS SQL
        /// </summary>
        /// <returns>Return File Name</returns>
        public override string ToString()
        {
            return this.File;
        }
        /// <summary>
        /// Read important options from stream.
        /// </summary>
        /// <param name="r"></param>
        public void Read(System.IO.BinaryReader r)
        {
            this._IsNull = r.ReadBoolean();
            if (this.IsNull == false)
            {
                this.File = r.ReadString();
                this._Password = r.ReadString();
                this._Comment = r.ReadString();
                this._CompressionLevel = r.ReadByte();
                this._CompressionMethod = r.ReadByte();
                this._EncryptionAlgorithm = r.ReadByte();
                this._ExtractionAction = r.ReadByte();
            }
        }
        /// <summary>
        /// Write important options into stream. This garantie that the data are not going lost.
        /// </summary>
        /// <param name="w"></param>
        public void Write(System.IO.BinaryWriter w)
        {
            w.Write(this.IsNull);
            if (this.IsNull == false)
            {
                w.Write(this.File);
                w.Write(this._Password.ValueOrEmpty());
                w.Write(this._Comment.ValueOrEmpty());
                w.Write(this._CompressionLevel);
                w.Write(this._CompressionMethod);
                w.Write(this._EncryptionAlgorithm);
                w.Write(this._ExtractionAction);

            }
            this.Dispose();
        }
        /// <summary>
        /// INullable interface.
        /// </summary>
        public bool IsNull
        {
            get { return this._IsNull; }
        }
        /// <summary>
        /// Can compare two different ZipFile.
        /// </summary>
        /// <param name="other">Other ZipFile to compare with.</param>
        /// <returns>True if equals.</returns>
        public bool Equals(DotNetZipStruct other)
        {
            throw new NotImplementedException();
        }
        /// <summary>
        /// Redirect default Object Equals to IEquatable Interface.
        /// </summary>
        /// <param name="obj">Object to compare</param>
        /// <returns>false if object == null else see Equals(DotNetZipStruct)</returns>
        public override bool Equals(object obj)
        {
            return obj == null ? false : this.Equals((DotNetZipStruct)obj);
        }
        /// <summary>
        /// Generate Hashcode from File if the value not null or empty.
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return String.IsNullOrEmpty(this.File) == false ? this.File.GetHashCode() : 0;
        }
        #endregion
        /// <summary>
        /// Parse
        /// </summary>
        /// <param name="str">String to Parse</param>
        /// <returns>Zip Struct</returns>
        public static DotNetZipStruct Parse(SqlString str)
        {
            DotNetZipStruct result = Null;

            if (str.IsNullOrEmpty() == false)
            {
                result = new DotNetZipStruct
                {
                    _File = str.Value,
                    _IsNull = false
                };
            }
            return result;
        }
        /// <summary>
        /// Return Null valued ZIP Struct.
        /// </summary>
        public static DotNetZipStruct Null
        {
            get
            {
                return new DotNetZipStruct()
                {
                    _IsNull = true,
                    _File = null
                };
            }
        }
        /// <summary>
        /// Get Type Information
        /// </summary>
        /// <returns>String of HowTo use this Data Type.</returns>
        [return: SqlFacet(MaxSize = -1)]
        public static String Help()
        {
            return Null.HowTo();
        }
        /// <summary>
        /// Generate CLR Creation code, since struct cannot inherit from class.
        /// </summary>
        /// <returns></returns>
        public ClrExportString GetClrCreationCode()
        {
            return ClrBase.GetClrCreationCode(this.GetType());
        }
    }
}


